﻿using Autofac;
using Autofac.Builder;
using Autofac.Core;
using CleanArchitecture.Core;
using CleanArchitecture.Core.Configuration;
using CleanArchitecture.Core.Handlers;
using CleanArchitecture.Core.Helper;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Core.Interfaces.Cache;
using CleanArchitecture.Core.Interfaces.DependencyManagement;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.AdminUsers;
using CleanArchitecture.Infrastructure.Interfaces.Authentication;
using CleanArchitecture.Infrastructure.Interfaces.Common;
using CleanArchitecture.Infrastructure.Interfaces.Media;
using CleanArchitecture.Infrastructure.Interfaces.Notice;
using CleanArchitecture.Infrastructure.Interfaces.Security;
using CleanArchitecture.Infrastructure.Interfaces.SystemLog;
using CleanArchitecture.Infrastructure.Interfaces.SystemManage;
using CleanArchitecture.Infrastructure.Interfaces.SystemTasks;
using CleanArchitecture.Infrastructure.Services;
using CleanArchitecture.Infrastructure.Services.AdminUsers;
using CleanArchitecture.Infrastructure.Services.Authentication;
using CleanArchitecture.Infrastructure.Services.Common;
using CleanArchitecture.Infrastructure.Services.Media;
using CleanArchitecture.Infrastructure.Services.Notice;
using CleanArchitecture.Infrastructure.Services.Security;
using CleanArchitecture.Infrastructure.Services.SystemLog;
using CleanArchitecture.Infrastructure.Services.SystemManage;
using CleanArchitecture.Infrastructure.Services.SystemTasks;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace CleanArchitecture.Infrastructure
{
    /// <summary>
    /// Dependency registrar
    /// </summary>
    public class CoreDependencyRegistrar : IDependencyRegistrar
    {
        /// <summary>
        /// Register services and interfaces
        /// 框架核心组件Ioc在此注册
        /// 涉及业务模务模块Interface/Implement需要在业务层继承IDependencyRegistrar重写Register注册
        /// </summary>
        /// <param name="builder">Container builder</param>
        /// <param name="typeFinder">Type finder</param>
        /// <param name="config">Config</param>
        public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NowaConfig config)
        {
            //file provider
            //DavidLee 2019/11/21 abandoned NowaFileProvider Ctor with IHostingEnvironment parametor
            builder.Register(context => CommonHelper.DefaultFileProvider).As<INowaFileProvider>().InstancePerLifetimeScope();

            //web helper
            builder.RegisterType<WebHelper>().As<IWebHelper>().InstancePerLifetimeScope();

            //data layer
            builder.RegisterType<EfDataProviderManager>().As<IDataProviderManager>().InstancePerDependency();
            builder.Register(context => context.Resolve<IDataProviderManager>().DataProvider).As<IDataProvider>().InstancePerDependency();
            builder.Register(context => new AppDbObjectContext(context.Resolve<DbContextOptions<AppDbObjectContext>>()))
                .As<IDbContext>().InstancePerLifetimeScope();

            //repositories
            builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
            //https://stackoverflow.com/questions/8190480/how-to-pass-multiple-expressions-to-orderby-for-ef
            builder.RegisterGeneric(typeof(OrderByExpression<,>)).As(typeof(IOrderByExpression<>)).InstancePerLifetimeScope();

            //cache manager
            builder.RegisterType<PerRequestCacheManager>().As<ICacheManager>().InstancePerLifetimeScope();

            //static cache manager
            if (config.RedisCachingEnabled)
            {
                //builder.RegisterType<RedisConnectionWrapper>()
                //    .As<ILocker>()
                //    .As<IRedisConnectionWrapper>()
                //    .SingleInstance();
                //builder.RegisterType<RedisCacheManager>().As<IStaticCacheManager>().InstancePerLifetimeScope();
                throw new NowaException("Redis Support is currently not implemented yet!");
            }
            else
            {
                builder.RegisterType<EasyMemoryCacheManager/*MemoryCacheManager*/>()
                    .As<ILocker>()
                    .As<IStaticCacheManager>()
                    .SingleInstance();
            }

            builder.RegisterType<ActionContextAccessor>().As<IActionContextAccessor>().InstancePerLifetimeScope();
            //work context
            builder.RegisterType<WebWorkContext>().As<IWorkContext>().InstancePerLifetimeScope();
            //RouteConfig
            builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().SingleInstance(); 
            
            builder.RegisterType<SettingService>().As<ISettingService>().InstancePerLifetimeScope();
            //if(config.QiniuCloudStorageEnabled)
            //    builder.RegisterType<QiniuPictureService>().As<IPictureService>().InstancePerLifetimeScope();
            //else
            builder.RegisterType<FileUploadSerive>().As<IFileUploadService>().InstancePerLifetimeScope();

            //roxy file manager service
            //builder.Register(context =>
            //{
            //    var pictureService = context.Resolve<IFileUploadService>();

            //    return EngineContext.Current.ResolveUnregistered(typeof(DatabaseRoxyFilemanService));

            //}).As<IRoxyFilemanService>().InstancePerLifetimeScope();
            //register all settings
            builder.RegisterSource(new SettingsSource());

            //doaminEvent Dispatch services
            builder.RegisterType<DomainEventDispatcher>()
                .As<IDomainEventDispatcher>().SingleInstance();
            //event handlers
            var handlers = typeFinder.FindClassesOfType(typeof(IHandle<>)).ToList();
            foreach (var hlr in handlers)
            {
                builder.RegisterType(hlr)
                    .As(hlr.FindInterfaces((type, criteria) =>
                    {
                        var isMatch = type.IsGenericType && ((Type)criteria).IsAssignableFrom(type.GetGenericTypeDefinition());
                        return isMatch;
                    }, typeof(IHandle<>)))
                    .InstancePerLifetimeScope();
            }

            //DavidLee 2020/06/22 For ScheduleTask with Quartz.NET support
            builder.RegisterType<StdSchedulerFactory>().As<ISchedulerFactory>().InstancePerLifetimeScope();
            builder.RegisterType<IOCJobFactory>().As<IJobFactory>().InstancePerLifetimeScope();
            builder.RegisterType<TaskThread>().AsSelf().InstancePerLifetimeScope();
            //所有需要自动执行的任务需要通过Autofac注册，否则无法IOC实例化通过接口调用
            //https://stackoverflow.com/questions/15449118/the-requested-service-has-not-been-registered-autofac-dependency-injection
            builder.RegisterType<ClearLogTask>().AsSelf().InstancePerLifetimeScope();
            //Services
            builder.RegisterType<DateTimeHelper>().As<IDateTimeHelper>().InstancePerLifetimeScope();
            builder.RegisterType<SystemTaskService>().As<ISystemTaskService>().InstancePerLifetimeScope();
            builder.RegisterType<DbSystemLogService>().As<ISystemLogService>().InstancePerLifetimeScope();
            builder.RegisterType<AuthenticationService>().As<IAuthenticationService>().InstancePerLifetimeScope();
            builder.RegisterType<EncryptionService>().As<IEncryptionService>().InstancePerLifetimeScope();
            builder.RegisterType<AdminUserService>().As<IAdminUserService>().InstancePerLifetimeScope();
            builder.RegisterType<AdminUserRegistrationService>().As<IAdminUserRegistrationService>().InstancePerLifetimeScope();
            builder.RegisterType<SystemModuleSevice>().As<ISystemModuleService>().InstancePerLifetimeScope();
            builder.RegisterType<PermissionService>().As<IPermissionService>().InstancePerLifetimeScope();
            builder.RegisterType<GenericAttributeService>().As<IGenericAttributeService>().InstancePerLifetimeScope();
            builder.RegisterType<SystemNoticeService>().As<ISystemNoticeService>().InstancePerLifetimeScope();
            builder.RegisterType<SystemDataDicService>().As<ISystemDataDicService>().InstancePerLifetimeScope();
        }

        /// <summary>
        /// Gets order of this dependency registrar implementation
        /// </summary>
        public int Order
        {
            get { return 1; }
        }
    }

    
    /// <summary>
    /// Setting source
    /// </summary>
    public class SettingsSource : IRegistrationSource
    {
        static readonly MethodInfo BuildMethod = typeof(SettingsSource).GetMethod(
            "BuildRegistration",
            BindingFlags.Static | BindingFlags.NonPublic);

        #region 依赖库升级接口变更
        /// <summary>
        /// Registrations for
        /// </summary>
        /// <param name="service">Service</param>
        /// <param name="registrations">Registrations</param>
        /// <returns>Registrations</returns>
        //public IEnumerable<IComponentRegistration> RegistrationsFor(
        //    Service service,
        //    Func<Service, IEnumerable<IComponentRegistration>> registrations)
        //{
        //    var ts = service as TypedService;
        //    if (ts != null && typeof(ISettings).IsAssignableFrom(ts.ServiceType))
        //    {
        //        var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType);
        //        yield return (IComponentRegistration)buildMethod.Invoke(null, null);
        //    }
        //}
        #endregion

        static IComponentRegistration BuildRegistration<TSettings>() where TSettings : ISettings, new()
        {
            return RegistrationBuilder
                .ForDelegate((c, p) =>
                {
                    return c.Resolve<ISettingService>().LoadSetting<TSettings>();
                }).InstancePerLifetimeScope()
                .CreateRegistration();
        }

        public IEnumerable<IComponentRegistration> RegistrationsFor(
            Service service,
            Func<Service, IEnumerable<ServiceRegistration>> registrationAccessor)
        {
            var ts = service as TypedService;
            if (ts != null && typeof(ISettings).IsAssignableFrom(ts.ServiceType))
            {
                var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType);
                yield return (IComponentRegistration)buildMethod.Invoke(null, null);
            }
        }

        /// <summary>
        /// Is adapter for individual components
        /// </summary>
        public bool IsAdapterForIndividualComponents { get { return false; } }
    }

}
